<?php

ini_set('default_socket_timeout', -1);

class RedisStruct {
  private $name;
  protected $ttl;

  private static $client;
  private static $mapping = [
    'RCounter' => 'counter',
    'Firewall' => 'counter',
    'InstantCounter' => 'counter',
    'ListingSet' => 'listing',
  ];

  /**
   * @param string $name 自己保证不要和别人的重复哦~
   * @param int $ttl 过期时间
   */
  public function __construct($name, $ttl) {
    $this->name = $name;
    $this->ttl = $ttl;
  }

  /**
   * @return Redis
   */
  protected function client() {
    if (is_null(self::$client)) {
      $config = Config::get('env.redis.' . self::$mapping[get_class($this)]);
      self::$client = new RedisWrapper($config['host'], $config['port'], 3.0);
    }
    return self::$client;
  }

  /**
   * @return string key for redis
   */
  protected function name() {
    return $this->name;
  }

  /**
   * @return int 删除对应的数据
   */
  public function delete() {
    $this->client()->delete($this->name);
  }

  /**
   * @return int 返回数据是否存在
   */
  public function exists() {
    return $this->client()->exists($this->name);
  }

  /**
   * @return int 返回距离过期还有多少秒
   */
  public function ttl() {
    return $this->client()->ttl($this->name);
  }

  /**
   * @param int $ttl 过期时间
   * @return int
   */
  public function set_ttl($ttl) {
    if ($ttl < 0) {
      return $this->client()->persist($this->name);
    } else {
      return $this->client()->expire($this->name, $ttl);
    }
  }

  /**
   * @return int
   */
  public function refresh() {
    return $this->set_ttl($this->ttl);
  }
}

class RedisWrapper {
  private $client,
    $host,
    $port,
    $time_out;

  public function __construct($host, $port, $time_out) {
    $this->host = $host;
    $this->port = $port;
    $this->time_out = $time_out;

    $this->client = new Redis();
    $this->client->pconnect($host, $port, $time_out);
  }

  public function __call($name, $args) {
    //retry 2
    for ($i = 0; $i < 5; $i++) {
      try {
        $return = call_user_func_array([$this->client, $name], $args);
      } catch (Exception $e) {
        $this->client->pconnect($this->host, $this->port, $this->time_out);
        continue;
      }
      return $return;
    }
    throw $e;
  }
}